package ar.pegasus.clasificador.engine.buscador;

import java.util.HashMap;
import java.util.List;

import ar.pegasus.clasificador.dao.api.CalleManager;
import ar.pegasus.clasificador.dao.api.LocalidadManager;
import ar.pegasus.clasificador.dao.api.local.LocalidadDAOLocal;
import ar.pegasus.clasificador.dao.api.local.SeccionDAOLocal;
import ar.pegasus.clasificador.entidades.Localidad;
import ar.pegasus.clasificador.entidades.Seccion;
import ar.pegasus.clasificador.entidades.TramoCalle;
import ar.pegasus.clasificador.entidades.wrappers.CalleWrapper;
import ar.pegasus.clasificador.entidades.wrappers.InterseccionWrapper;
import ar.pegasus.clasificador.entidades.wrappers.TramoCalleWrapper;
import ar.pegasus.clasificador.enums.EnumValorBoolean;
import ar.pegasus.clasificador.excepciones.CalleInexistenteException;
import ar.pegasus.clasificador.excepciones.CalleSinAlturasException;
import ar.pegasus.clasificador.excepciones.DatosIncompletosException;
import ar.pegasus.clasificador.excepciones.LocalidadMalFormadaException;
import ar.pegasus.clasificador.util.BeanFactoryClasificadorLocal;
import ar.pegasus.clasificador.util.CalleParser;
import ar.pegasus.clasificador.util.LocalidadParser;
import ar.pegasus.clasificador.util.NombreScanner;
import ar.pegasus.clasificador.util.NombreToken;
import ar.pegasus.framework.componentes.PException;
import ar.pegasus.framework.util.NumUtil;


public class BuscadorCircuitoManager {

	private static BuscadorCircuitoManager instance;
	
	public static BuscadorCircuitoManager getInstance() throws PException {
		if(instance == null)
			instance = new BuscadorCircuitoManager();
		return instance;
	}
	// Acceso a datoss
	private SeccionDAOLocal seccionDAO;
	private LocalidadDAOLocal localidadDAO;
	// Atributos 
	private HashMap<String,Seccion> secciones = new HashMap<String, Seccion>();
//	private int errores;
	private String localidad_buscada;
	private String localidad_hallada;
	private int cod_localidad_buscada;
	private String circuito_hallado;
	private EnumValorBoolean[] existen_calles = new EnumValorBoolean[4];
	private Object[] calles_halladas = new Object[3]; 
	
	private BuscadorCircuitoManager() throws PException{
		seccionDAO = BeanFactoryClasificadorLocal.getInstance().getBean(SeccionDAOLocal.class);
		localidadDAO = BeanFactoryClasificadorLocal.getInstance().getBean(LocalidadDAOLocal.class);
	}
	
	/**
	 * Este método se encarga de recuperar el circuito electoral de acuerdo con los datos recibidos de localidad y domicilio
	 * y el criterio de busquedad de la seccion.
	 * 
	 * @param seccion
	 * @param domicilio
	 * @param localidad
	 * @return
	 */
	private String buscarCircuito(Seccion seccion, String domicilio, String localidad) throws PException {
		// validamos la completitud de los datos buscados
		if(seccion.getCriterio().equals(CriteriosBusqueda.CALLE) && domicilio.isEmpty())
			throw new DatosIncompletosException("La calle no puede omitirse para este criterio de clasificación");
		if( seccion.getCriterio().equals(CriteriosBusqueda.LOCALIDAD) && localidad.isEmpty())
			throw new DatosIncompletosException("La Localidad no puede omitirse para este criterio de clasificación");
		
		// parseo las localidades
		existen_calles[1] = EnumValorBoolean.Null;
		existen_calles[2] = EnumValorBoolean.Null;
		existen_calles[3] = EnumValorBoolean.Null;

		// Ahora empezamos a efectuar la busqueda.
		int cantlocalidades = parsearLocalidad(seccion, localidad);

		// Analizo el criterio de busqueda, para mayor eficencia, se analiza si hay una localidad
		// para luego determinar si se buscara asociada o no con la calle.
		if(cantlocalidades != 0){ 
			if( seccion.getCriterio().equals(CriteriosBusqueda.LOCALIDAD))
				circuito_hallado = buscarCriterioLocalidad(seccion, domicilio);
			else
				circuito_hallado = buscarDireccionConLocalidad(seccion, domicilio);
		}else
			circuito_hallado = buscarCriterioCalle(seccion, domicilio);

		return circuito_hallado;
	}

	/**
	 * modifica: la variable circuito_hallado.
	 * @param seccion
	 * @param domicilio un string, que es la calle original (que es la calle ya parseada )
	 * @return el circuito electoral de acuerdo a los datos ingresados.
	 * @throws PException 
	 */
	private String buscarCriterioLocalidad(Seccion seccion, String domicilio) throws PException {
		// trato de obtener las calles que componen el domicilio
		CalleParser parser = new CalleParser(seccion,domicilio);
		
//		cantCallesParseadas = parsing(calle_original,calles,seccion)
		int cantCallesParseadas = parser.parse();

		// veo si la calle lleva el nombre con que está declarada	
		switch(cantCallesParseadas){
			case 0:
				return buscarLocalidad(seccion, localidad_hallada);
			
			case 1:
				// calle sin altura y con localidad.		
				if(!seleccionaCalles(1, parser.getCallesHalladas(), seccion))
					return buscarLocalidad(seccion, localidad_hallada);
				circuito_hallado = buscarCalleConLocalidad(seccion);
				break;
			case 2:
				// dos calles, por lo cual deberé aplicar una busca
				circuito_hallado = buscarInterseccionLocal(parser.getCallesHalladas(),seccion);
				break;
			case 3:
				// tres calle, sin altura.
				circuito_hallado = buscarEntreCallesLocal(parser.getCallesHalladas(),seccion);
				break;
			case 4:
				// Si el valor de cantCallesParseadas es 0, entonces la calle tiene altura.
				if(!seleccionaCalles(1, parser.getCallesHalladas(), seccion))
					return buscarLocalidad(seccion, localidad_hallada);
				circuito_hallado = buscarCalleConLocalidadAltura(parser.getCallesHalladas(),seccion);
				break;
		}
//		if (circuito_hallado != null)
		return circuito_hallado; 
//		return buscarLocalidad(seccion, localidad_hallada);
	}


	/**
	 * Se encarga de determinar el circuito solamente con los datos de la/s calle/s.
	 * modifica: localidad_hallada, cod_calle (codigo de calle),el arreglo de calles.
	 * @param seccion  
	 * @param domicilio es la calle ingresada.
	 * @return un string que es circuito.
	 * @throws PException 
	 */
	private String buscarCriterioCalle(Seccion seccion, String domicilio) throws PException {
		int cantCallesParseadas;
		
		circuito_hallado = null;
		// trato de obtener las calles que componen el domicilio
		CalleParser parser = new CalleParser(seccion, domicilio);
		cantCallesParseadas = parser.parse();

		// veo si la calle lleva el nombre con que está declarada	
		switch(cantCallesParseadas){
			case 0:
				// se analiza el caso, de una calle con altura
				if (!seleccionaCalles(1,parser.getCallesHalladas(), seccion)){ 
					throw new CalleInexistenteException( "No existe la calle dentro de la seccion electoral "+ seccion.getNombre());
				}		
				return busquedaDirectaConAltura(parser.getCallesHalladas(),seccion);							
			// se analiza el caso, de una calle sin altura
			case 1:
				// se determina las posible calles
				if(!seleccionaCalles(1,parser.getCallesHalladas(), seccion)){ 
					throw new CalleInexistenteException( "No existe la calle dentro de la seccion electoral "+ seccion.getNombre());					
				}
				return busquedaDirecta(seccion);
			case 2:
				// analisis de intersecciones entre dos calles
				return buscarInterseccion(parser.getCallesHalladas(), seccion);
			case 3:
				// analisis de intersecciones entre tres calles
				return buscarEntreCalles(parser.getCallesHalladas(), seccion);
		}
		throw new CalleInexistenteException( "No existe la calle dentro de la seccion electoral "+ seccion.getNombre());
	}

	/**
	 * Funcion: determinar el circuito de un domicilio con dos calles y la localidad.
	 * requiere: una lista de string, en donde estan las dos calles.
	 * modifica: la variable circuito hallado y errores.
	 * @param calles
	 * @param seccion
	 * @return un string, que es el circuito del domicilio.
	 * @throws PException 
	 */
	@SuppressWarnings("unchecked")
	private String buscarInterseccionLocal(String[] calles, Seccion seccion) throws PException {
		String ultimo_circuito_hallado = null;

		// recupero todas las calles parecidas a calles[1]
		if (!seleccionaCalles(1, calles, seccion))
			// Si no existe la calle principal, entonces lanzar error y terminar clasificacion.
			throw new CalleInexistenteException("No se puede determinar el circuito, por no existir la calle principal");

		// recupero todas las calles parecidas a calles[2]
		if (!seleccionaCalles(2, calles, seccion )) {
			// Si no tiene calle secundaria podria ejecutar la busqueda por calle y localidad.
//			throw new CalleInexistenteException("No se puede determinar el circuito, por no existir la calle secundaria");
			return buscarCalleConLocalidad(seccion);
		}

		//Por cada calle principal recorro las intersecciones posibles.
		for(CalleWrapper callePrincipal : (List<CalleWrapper>)calles_halladas[0]){
			
			for(CalleWrapper calleSecundaria : (List<CalleWrapper>)calles_halladas[1]){
				// Obtenemos las intersecciones.
				List<InterseccionWrapper> tramos = CalleManager.getInstance().getInterseccionesPorLocalidad(seccion, callePrincipal.getCodigo(), calleSecundaria.getCodigo());

				for(InterseccionWrapper interseccion : tramos){
					// veo si la interseccion pertenecia a la localidad buscada
					circuito_hallado = interseccion.getCircuito();
					if(perteneceAlocalidad(cod_localidad_buscada,interseccion.getIdLocalidad())){ 
						return circuito_hallado;
					}else{
						if(ultimo_circuito_hallado == null) 
							ultimo_circuito_hallado=circuito_hallado;
					}
				}
			}
		}
		// analisis del ultimo_circuito_hallado, pero no se sabe si una de las calles no pertenece a 
		// la localidad.
		if (ultimo_circuito_hallado == null){ 
			return buscarCalleConLocalidad(seccion);
		}
		return ultimo_circuito_hallado;
	}
	
	/**
	 * Funcion: determinar el circuito de un cruces
	 * requiere: dos string, que son las calles parseadas
	 * modifica: las variables errores y el circuito hallado
	 * @param calles
	 * @param seccion
	 * @return un string que es el circuito hallado
	 * @throws PException 
	 */
	@SuppressWarnings("unchecked")
	private String buscarInterseccion(String[] calles,Seccion seccion) throws PException {
		// recupero todas las calles parecidas a calles[1]
		if (!seleccionaCalles(1, calles, seccion))
			// Si no existe la calle principal, entonces lanzar error y terminar clasificacion.
			throw new CalleInexistenteException("No se puede determinar el circuito, por no existir la calle principal");

		if(!seleccionaCalles(2, calles, seccion )){
			// se decide clasificar por una calle, ya que la segunda no existe	
			return busquedaDirecta(seccion);
		}

		//Por cada calle principal recorro las intersecciones posibles.
		for(CalleWrapper callePrincipal : (List<CalleWrapper>)calles_halladas[0]){
			
			for(CalleWrapper calleSecundaria : (List<CalleWrapper>)calles_halladas[1]){
				// Obtenemos las intersecciones.
				List<InterseccionWrapper> tramos = CalleManager.getInstance().getIntersecciones(seccion, callePrincipal.getCodigo(), calleSecundaria.getCodigo());

				for(InterseccionWrapper interseccion : tramos){
					// veo si la interseccion pertenecia a la localidad buscada
					circuito_hallado = interseccion.getCircuito();
					return circuito_hallado;
				}
			}
		}
			
		// se acabaron las intersecciones y no halle una que cumpliera con lo buscado
		// sin embargo se que por lo menos ambas calles existen
		return busquedaDirecta(seccion);
	}

	
	/**
	 * Funcion:  determinar el circuito de un domicilio declarado con tres calles.
	 * requiere: tres string, que son las calles parseadas.
	 * modifica: las variables circuitos hallados y errores.
	 * 
	 * @param seccion
	 * @return un string que es el circuito hallado.
	 * @throws PException 
	 */
	@SuppressWarnings("unchecked")
	private String buscarEntreCallesLocal(String[] calles,Seccion seccion) throws PException {
		String ultimo_circuito_hallado = null;

		// recupero todas las calles parecidas a calles[1]
		if (!seleccionaCalles(1, calles, seccion))
			// Si no existe la calle principal, entonces lanzar error y terminar clasificacion.
			throw new CalleInexistenteException("No se puede determinar el circuito, por no existir la calle principal");

		// recupero todas las calles parecidas a calles[2]
		if (!seleccionaCalles(2, calles, seccion )) {
			// Si no tiene calle secundaria podria ejecutar la busqueda por calle y localidad.
//			throw new CalleInexistenteException("No se puede determinar el circuito, por no existir la calle secundaria");
			return buscarCalleConLocalidad(seccion);
		}

		// recupero todas las calles parecidas a calles[3]
		if (!seleccionaCalles(3, calles, seccion)) 
//			errores =  -3003
			return buscarInterseccionLocal(calles, seccion);

		// analisis,deteccion y seleccion del tramo que cumpla con todas las condiciones
		//Por cada calle principal recorro las intersecciones posibles.
		for(CalleWrapper callePrincipal : (List<CalleWrapper>)calles_halladas[0]){
			
			// Obtenemos la primera perpendicular.
			for(CalleWrapper calleSecundaria : (List<CalleWrapper>)calles_halladas[1]){
				
				// Iteramos por la segunda perpendicular
				for(CalleWrapper calleTerciaria : (List<CalleWrapper>)calles_halladas[1]){
					
					List<InterseccionWrapper> tramos = CalleManager.getInstance().getEntreCallesPorLocalidad(seccion, callePrincipal.getCodigo(), calleSecundaria.getCodigo(),calleTerciaria.getCodigo());

					for(InterseccionWrapper interseccion : tramos){
						// veo si la interseccion pertenecia a la localidad buscada
						circuito_hallado = interseccion.getCircuito();
						if(perteneceAlocalidad(cod_localidad_buscada,interseccion.getIdLocalidad())){ 
							return circuito_hallado;
						}else{
							if(ultimo_circuito_hallado == null) 
								ultimo_circuito_hallado=circuito_hallado;
						}
					}
				}
			}
		}
		// busco las dos calles
		if(ultimo_circuito_hallado == null) return buscarInterseccionLocal(calles, seccion);
		return ultimo_circuito_hallado;
	}
	
	/**
	 * Funcion:  determinar el circuito de un domicilio declarado con tres calles.
	 * requiere: tres string, que son las calles parseadas.
	 * modifica: las variables circuitos hallados y errores.
	 * @param calles
	 * @param seccion
	 * @return   un string que es el circuito hallado.
	 * @throws PException 
	 */
	@SuppressWarnings("unchecked")
	private String buscarEntreCalles(String[] calles,Seccion seccion) throws PException {
		// recupero todas las calles parecidas a calles[1]
 		if (!seleccionaCalles(1, calles, seccion))
			// Si no existe la calle principal, entonces lanzar error y terminar clasificacion.
			throw new CalleInexistenteException("No se puede determinar el circuito, por no existir la calle principal");

		// recupero todas las calles parecidas a calles[2]
		if (!seleccionaCalles(2, calles, seccion)){
			return busquedaDirecta(seccion);	
		}

		// recupero todas las calles parecidas a calles[3]
		if (!seleccionaCalles(2, calles, seccion))
			return buscarInterseccion(calles, seccion);

		// analisis,deteccion y seleccion del tramo que cumpla con todas las condiciones
		//Por cada calle principal recorro las intersecciones posibles.
		for(CalleWrapper callePrincipal : (List<CalleWrapper>)calles_halladas[0]){
			
			// Obtenemos la primera perpendicular.
			for(CalleWrapper calleSecundaria : (List<CalleWrapper>)calles_halladas[1]){
				
				// Iteramos por la segunda perpendicular
				for(CalleWrapper calleTerciaria : (List<CalleWrapper>)calles_halladas[1]){
					
					List<InterseccionWrapper> tramos = CalleManager.getInstance().getEntreCalles(seccion, callePrincipal.getCodigo(), calleSecundaria.getCodigo(),calleTerciaria.getCodigo());

					for(InterseccionWrapper interseccion : tramos){
						// veo si la interseccion pertenecia a la localidad buscada
						circuito_hallado = interseccion.getCircuito();
						return circuito_hallado;
					}
					
				}
			}
		}

		// no hay interseccion entre las tres calles, entonces se analiza si entre la primer y 
		// segunda calle existe una interseccion.
		return buscarInterseccion(calles, seccion);
	}


	/**
	 * requiere: un string que es el domicilio que declara el usuario, este consiste de 
	 * calle y su altura.
	 * modifica: las variables errores y circuito_hallado.
	 * @param calles 
	 * @return el circuito hallado.
	 * @throws PException 
	 */
	@SuppressWarnings("unchecked")
	private String buscarCalleConLocalidadAltura(String[] calles,Seccion seccion) throws PException {
		//Este cursor sirve para las calles que tienen altura y localidad
		long alturaCalle;
		String ultimoCircuitoHallado, paridad;

		//analisis de los resultados	
		alturaCalle = Integer.getInteger(calles[4]);

		ultimoCircuitoHallado = null;
		paridad = (NumUtil.esPar(alturaCalle))?"P":"I";
		
		// recorremos la lista de calles y sinonimos 
		for(CalleWrapper calleEncontrada : (List<CalleWrapper>)calles_halladas[0]){
			// Obtenemos todos los tramos de la Seccion para la calle buscada que cubran la altura y paridad
			List<TramoCalleWrapper> tramos = CalleManager.getInstance().getTramosCalleConLocalidadAltura(seccion, calleEncontrada.getCodigo(), alturaCalle, paridad);
			
			for(TramoCalleWrapper tramoCalle : tramos){
				if( perteneceAlocalidad( cod_localidad_buscada, tramoCalle.getIdLocalidad()))
					return tramoCalle.getCircuito();
				else
					if(ultimoCircuitoHallado == null)
						ultimoCircuitoHallado = tramoCalle.getCircuito();
			}
			
		}
		// Si no se pudo hallar un circuito, entonces alternar los criterios para buscar por calles.
		if(ultimoCircuitoHallado == null){ 
			if(seccion.getCriterio().equals(CriteriosBusqueda.LOCALIDAD)) 
				return buscarLocalidad(seccion, localidad_hallada);
			return buscarCalleConLocalidad(seccion);
		}	
		return ultimoCircuitoHallado;
	}

	/**
	 * Funcion: determinar el circuito de acuerdo a los datos, pero sin cambiar a pesar de tener localidad el criterio.
	 * requiere: que el vector calles halladas haya sido cargado ( direccion ingresada ) de 1 es y que exista la localidad.
	 *           para esto se debe haber llamado a seleccionaCalle().
	 * NOTA: 
	 *  - el circuito que se hallara, pertenece a una calle sin altura.
	 *  - en el caso, que no se encuentre un tramo que pertenezca a la localidad enunciada,
	 *    se tomara como valido el nombre de la calle, y en el caso que halla varias con el mismo
	 *    nombre, se tomara la primera.
	 *  - se toma como valida el circuito del primer tramo.		
	 * modifica: la variable error, si eventualmente lo hubo, y la variable circuito_hallado.
	 * 
	 * @param seccion
	 * @return el circuito_hallado.
	 * @throws PException 
	 */
	@SuppressWarnings("unchecked")
	private String buscarCalleConLocalidad(Seccion seccion) throws PException {
		String ultimo_circuito_hallado = null;
		
		// recorremos la lista de calles y sinonimos 
		for(CalleWrapper calleEncontrada : (List<CalleWrapper>)calles_halladas[0]){
			List<TramoCalleWrapper> tramos = CalleManager.getInstance().getTramosCalle(seccion, calleEncontrada.getCodigo());

			for(TramoCalleWrapper tramo : tramos){
				if (perteneceAlocalidad( cod_localidad_buscada,tramo.getIdLocalidad())){
					return tramo.getCircuito();
				}else
					if (ultimo_circuito_hallado == null) ultimo_circuito_hallado = tramo.getCircuito();
			}
		}
		if(ultimo_circuito_hallado == null){ 
			return buscarLocalidad(seccion, localidad_hallada);
		}	
		return ultimo_circuito_hallado;

	}
	
	/**
	 * Efectua la busqueda del Circuito electoral a partir del domicilio informado, el cual no tiene numero o altura.
	 * 
	 * @param seccion seccion dentro de la cual buscar
	 * @return circuito hallado
	 * @throws PException 
	 */
	@SuppressWarnings("unchecked")
	private String busquedaDirecta(Seccion seccion) throws PException {
		// Recorremos la lista de calles recuperadas en el metodo selecciona_calles()
		for(CalleWrapper calleEncontrada : (List<CalleWrapper>)calles_halladas[0]){
			// Obtenemos todos los tramos de la Seccion para la calle buscada que cubran la altura y paridad
			List<TramoCalle> tramos = CalleManager.getInstance().getTramosCalleByIdCalle(seccion,calleEncontrada.getCodigo());
			
			if(tramos.isEmpty())
				throw new CalleSinAlturasException("no se definieron las alturas para la calle cuyo codigo es: " + calleEncontrada.getCodigo());
			TramoCalle tramo = tramos.get(0);
			circuito_hallado = tramo.getCircuito();
			
		}
		return circuito_hallado;
	}
	
	/**
	 * Se encarga de efectuar una busqueda sobre la base con la direccion deseada que contiene tambien la altura
	 * 
	 * @return circuito hallado.
	 * @throws PException 
	 */
	@SuppressWarnings("unchecked")
	private String busquedaDirectaConAltura(String[] calles,Seccion seccion) throws PException {
		long 	 alturaCalle;
		String paridad;

		// Sabemos que existe la calle veo si tengo altura
		 alturaCalle = Integer.getInteger(calles[4]);
		 paridad = NumUtil.esPar(alturaCalle)?"P":"I";

		 for(CalleWrapper calleEncontrada : (List<CalleWrapper>)calles_halladas[0]){
				// Obtenemos todos los tramos de la Seccion para la calle buscada que cubran la altura y paridad
				List<TramoCalleWrapper> tramos = CalleManager.getInstance().getTramosCalleConAltura(seccion,calleEncontrada.getCodigo(),alturaCalle,paridad);
				
				if(!tramos.isEmpty()){
					TramoCalleWrapper tramo = tramos.get(0);
					circuito_hallado = tramo.getCircuito();
					return circuito_hallado;
				}
		 }
		 throw new CalleSinAlturasException("No existe la altura pedida para esa calle dentro de la seccion electoral "+ seccion);
		 
	}
	
	/**
	 * Determina si la localidad del tramo se encuentra dentro de la localidad buscada o dentro de alguna localidad superior. 
	 * @param idLocalidadBuscada
	 * @param idlocalidadActual
	 * @param idLocalidadPadre
	 * @return
	 * @throws PException 
	 */
	private boolean perteneceAlocalidad(int idLocalidadBuscada, int idlocalidadActual) throws PException {
		return LocalidadManager.getInstance().perteneceALocalidad(idLocalidadBuscada, idlocalidadActual);
	}

	/**
	 * Se encarga de buscar la la localidad dentro de la seccion dada y retornar el circuito. 
	 * Miramos si la fila correspondiente a la localidad buscada posee circuito.
	 * Siempre obtenemos la sublocalidad dado que la localidad cabecere tiene este como nulo y 
	 * se tiene a si misma como sublocalidad
	 * @param seccion La Seccion dentro de la que se busca.
	 * @param localidad La localidad buscada.
	 * @return el Circuito electoral de la localidad.
	 * @throws PException
	 */
	private String buscarLocalidad(Seccion seccion, String localidad) throws PException,LocalidadMalFormadaException {
		return LocalidadManager.getInstance().buscarLocalidad(seccion, localidad);
	}

	/**
	 * Busca a ver si ya se chequeo que la calle hallada exista, si ya se chequeo previamente
	 * entonces el valor se encuentra almacenado en existen_calles[calle_numero] y se retorna
	 * si en existen_calles[calle_numero] hay un valor Null, entonces busca en la base para validar
	 * si existe y retorna true.
	 * 
	 * @param calle_numero
	 * @param calles calles halladas para validar
	 * @return 
	 * @throws PException 
	 */
	private boolean seleccionaCalles(int calle_numero, String[] calles, Seccion seccion) throws PException {
		// Si el valor de existen_calles no es nulo, se deduce que esta funcion fue llamada por
		// lo menos una vez. Por lo tanto, retorno
		if(!EnumValorBoolean.isNull(existen_calles[calle_numero]) )
			return EnumValorBoolean.isTrue(existen_calles[calle_numero]);

		// Validamos que exista la calle, por eso no se devuelve el codigo.
		List<CalleWrapper> callesEncontradas = CalleManager.getInstance().getCalle(calles[calle_numero], 0, seccion);
		calles_halladas[calle_numero] = callesEncontradas;
		existen_calles[calle_numero] = EnumValorBoolean.getValor(!callesEncontradas.isEmpty());

		return EnumValorBoolean.isTrue(existen_calles[calle_numero]);
	}

	/**
	 * Determinar el circuito, sabiendo que existe la localidad pero el criterio es calle.
	 * requiere: dos string que representa la localidad y la calle con su altura.
	 * modifica: las variables errores y circuito_hallado. 
	 * @param seccion
	 * @param domicilio
	 * @return el circuito hallado.
	 * @throws PException 
	 */
	private String buscarDireccionConLocalidad(Seccion seccion, String domicilio) throws PException {
		circuito_hallado = null;
		
		//trato de obtener las calles que componen el domicilio
//		int cantCallesParseadas = parsing(domicilio,calles,seccion)
		CalleParser parser = new CalleParser(seccion, domicilio);
		int cantCallesParseadas = parser.parse();

		//veo si la calle lleva el nombre con que está declarada	
		switch(cantCallesParseadas){
		// se analiza el caso, de una calle con altura
			case 0:
				if(!seleccionaCalles(1,parser.getCallesHalladas(),seccion)){  
//					errores = -10000;
					return circuito_hallado;
				}
				return buscarCalleConLocalidadAltura(parser.getCallesHalladas(),seccion);							 
			// se analiza el caso, de una calle sin altura
			case 1:
				// se determina las posible calles
				if (!seleccionaCalles(1,parser.getCallesHalladas(),seccion))
					return buscarLocalidad(seccion,localidad_hallada);
				return buscarCalleConLocalidad(seccion);
			case 2:
				// analisis de intersecciones entre dos calles
				return buscarInterseccionLocal(parser.getCallesHalladas(),seccion);
			case 3:
				// analisis de intersecciones entre tres calles
				return buscarEntreCallesLocal(parser.getCallesHalladas(),seccion);
		}
//		errores = -1000;
		return circuito_hallado;
	}

	/**
	 * requiere: un string que es la localidad ingresada.
	 * modifica: la variable localidad_buscada, quedando en ella la localidad parseada,
	 * 			 y localidad_hallada queda la localidad ingresada.
	 * 
	 * @param localidad
	 * @return 0 si no existe la localidad, -1 Si hubo errores de parseo y 1 en el caso contrario.
	 * @throws PException 
	 */
	private int parsearLocalidad(Seccion seccion, String localidadOriginal) throws PException {
		int cantLocalidades;
		String[] localidades;// = new String[5]; //new ArrayList<String>();

		localidad_buscada = null;
		// en el caso que la localidad ingresada estuviera vacia se retorna 0
		if(localidadOriginal.trim().isEmpty())
			return 0;
		LocalidadParser parser = new LocalidadParser(seccion,localidadOriginal);
		cantLocalidades = parser.parse();
		localidades = parser.getLocalidadesHalladas();
//		cantLocalidades = parsing(localidadOriginal,localidades,seccion);
//		if(errores < 0) 
//			return errores;
		switch(cantLocalidades){
			case 0:
				localidad_buscada = localidades[0]+" "+localidades[4];
				break;
			case 1:
				localidad_buscada = localidades[0];
       			break;
			case 2:
				localidad_buscada = localidades[0]+" Y "+localidades[1];				
       			break;
		}
		cantLocalidades = 1;
		if(!existe_localidad(seccion, localidad_buscada))
			return 0;
		return 1;
	}

	/**
	 * Dado un nombre de Localidad candidato, en una Seccion, determina si esa localidad Existe y setea variables 
	 * de internas del objeto buscador.
	 * El criterio es el siguiente: Busca las localidades que cumplan con el nombre {@code localidadBuscada} tal cual se recibió
	 * Si existen alguna/s localidad/es que cumplan con este nombre, se elige la primera y se retorna ok.
	 * Si no existe una localidad que tenga <b>exactamente</b> el nombre buscado, se procede a desmantelar el nombre
	 * recibido en sus tokens y buscar si asi se encuentra alguna localidad que satisfaga la busqueda.
	 * 
	 * @param seccion Seccion sobre la que se efectua la busqueda.
	 * @param localidadBuscada Nombre de la localidad buscada 
	 * @return true si la localidad existe.s
	 * @throws PException 
	 */
	public boolean existe_localidad(Seccion seccion, String localidadBuscada) throws PException {
		String andcond,orcond;
		
		List<Localidad> localidadesEncontradas = localidadDAO.getLocalidades(seccion.getIdSeccion(), localidadBuscada);
		
		if(!localidadesEncontradas.isEmpty()){
		// si lo encontro, retorno en localidad_hallada el nombre real y en cod_localidad_buscada
		// el codigo de la locacalidad
			Localidad localidad = localidadesEncontradas.get(0);
			localidad_hallada = localidad.getNombre();
			cod_localidad_buscada = localidad.getIdLocalidad();
			return true;
		}
		// buscamos los tokens de la lista
//		tokenlist = localidad_buscada;
		NombreScanner scanner = new NombreScanner(localidad_buscada);
//		token = scanning(tokenlist, tipo);
		NombreToken token = scanner.getToken();
		
		// este es el primer token inicia la condicion debe ser el primero del nombre o del sinonimo
		if(token.getTipo() == 0) return false;
		andcond =" AND (((nombre LIKE '"+token+"%')";
		orcond = " AND (((sinonimo LIKE '"+token+"%')";

		// leemos el segundo token
		NombreToken ltoken = token;
//		token = scanning(tokenlist, tipo);
		token = scanner.getToken();
		while(token.getTipo() != 0){
			// leemos el tercer y subsiguientes tokens
			ltoken = token;
//			token = scanning( tokenlist,tipo );
			token = scanner.getToken();
			if( token.getTipo() != 0){
				if(ltoken.getToken().length() > 1){ 
					andcond = andcond + " AND (nombre LIKE '% " + ltoken.getToken() + " %')";
					orcond = orcond +" AND (sinonimo LIKE '% " + ltoken.getToken() + " %')";		
				}else{
					andcond = andcond + " AND (nombre LIKE '% " + ltoken.getToken() + "%')";
					orcond = orcond +" AND (sinonimo LIKE '% " + ltoken.getToken() + "%')";				
				}	
			}
		}
		// se acabaron los tokens por ello debo concatenar el token ya que todavia no se habia concatenado
		andcond = andcond + " AND (nombre LIKE '%" + ltoken.getToken() + "')";
		orcond = orcond +" AND (sinonimo LIKE '%" + ltoken.getToken() + "')";
		 
		// recuperamos la localidad que satisfaga la condicion.
		Localidad localidad = localidadDAO.getLocalidad(seccion.getIdSeccion(), andcond, orcond);
		
		if(localidad == null)
			return false;

		localidad_hallada = localidad.getNombre();

		// el segundo parametro es un nro pero depende de como se lo almacene en cada base
		cod_localidad_buscada = localidad.getIdLocalidad();

		return true;
	}

	/**
	 * Este metodo se encarga de recuperar el Circuito Electoral de acuerdo con el criterio aplicado para la Seccion
	 * recibida, localidad y domicilio.
	 * Los pasos a seguir son: 
	 * 
	 * 1. Con el nombre de la seccion, recuperar la Seccion.
	 * 2. Busca el circuito en base a la seccion, localidad y/o calle.
	 * @param nombreSeccion El nombre de la Sección (Municipio/Departamento/etc.)
	 * @param domicilio El domicilio del ciudadano.
	 * @param localidad La localidad
	 * @return
	 * @throws PException
	 */
	public String buscarCircuito(String nombreSeccion, String domicilio, String localidad) throws PException {
		return buscarCircuito(getSeccion(nombreSeccion.toLowerCase()),domicilio, localidad);
	}

	
	/**
	 * Retorna la seccion buscada en base al nombre de la seccion.
	 * 
	 * @param nombreSeccion
	 * @return
	 * @throws PException 
	 */
	private Seccion getSeccion(String nombreSeccion) throws PException {
		// Ver si no esta hasheada la seccion.
		if(secciones.containsKey(nombreSeccion))
			return secciones.get(nombreSeccion);
		// Si no esta en el hashmap, buscarlo en la base.
		Seccion seccion = seccionDAO.getByName(nombreSeccion);
		if(seccion != null){
			// agregar la seccion al hashmap.
			secciones.put(nombreSeccion, seccion);
		}
		return seccion;
	}

}
